.set				noreorder

.equ				TRAINER_OFFSET,	0x00400000

;ROM
;.org				0xB1FFE000
;RAM
;.org				0x80000000 + TRAINER_OFFSET

;.org				0x80000000 + TRAINER_OFFSET
SD	T3,	0x0070	(K0)	;
SD	T4,	0x0078	(K0)	;
SD	T5,	0x0080	(K0)	;
SD	T6,	0x0088	(K0)	;
SD	T7,	0x0090	(K0)	;
SD	S0,	0x0098	(K0)	;
SD	S1,	0x00A0	(K0)	;
SD	S2,	0x00A8	(K0)	;
SD	S3,	0x00B0	(K0)	;
SD	S4,	0x00B8	(K0)	;
SD	S5,	0x00C0	(K0)	;
SD	S6,	0x00C8	(K0)	;
SD	S7,	0x00D0	(K0)	;
SD	T8,	0x00D8	(K0)	;
SD	T9,	0x00E0	(K0)	;

LUI	T3,	%hi(0x80000000 + TRAINER_OFFSET)		;
LUI	T4,	0xC0DE		;
SW	T4,	%lo(0x7FFFFFFC + TRAINER_OFFSET)	(T3)	;
				;Signal to the trainer loader that the loading has been done

;;;	Begin trainer		;;;
;	V0-A3 and above stored regs are available

LUI	V0,	%hi(0x80000000 + TRAINER_OFFSET)		;Code base
ORI	V0,	V0,	%lo(0x80001000 + TRAINER_OFFSET)	;=0x80001000 + TRAINER_OFFSET
				Interpret_Loop:
LW	V1,	0x0000	(V0)	;First word
ADDIU	A1,	R0,	-0x0001	;0xFFFFFFFF == Code list terminator
BEQ	V1,	A1,		Exit
SRL	A0,	V1,	0x18	;Code type
;ANDI	A0,	A0,	0x00FF	;Should be unnecessary
ANDI	A1,	A0,	0x00F0	;Code type without sub type
LUI	A3,		0x0100	;
ADDIU	A3,	A3,	-0x0001	;
AND	V1,	V1,	A3	;Address (for most code types)
LUI	A3,		0x8000	;
OR	V1,	V1,	A3	;Address with top bit set
ORI	A2,	R0,	0x0080	;
BEQ	A1,	A2,		Type_8X
ORI	A2,	R0,	0x00D0	;
BEQ	A1,	A2,		Type_DX
ORI	A2,	R0,	0x00C0	;
BEQ	A1,	A2,		Type_CX
NOP
J				TRAINER_OFFSET + Interpret_Loop
ADDIU	V0,	V0,	0x0008	;Invalid code type

;Currently:
;V0 is interpreter pointer
;V1 is X - Address
;A0 is code type

;Interface
;S0 is I - Increment (amount to increment data)
;S1 is M - Mask
;S2 is N - Count
;S3 is O - Offset (increment amount for address)
;S4 is ? - Scratch (execution status (0 == false; anything else == true))
;S5 is Y - Data
;S6 is ? - Scratch (data at address)
;S7 is ? - Scratch (current data of code to process)

;-------------------------------;
;	WRITE CODES		;
;-------------------------------;
				Type_8X:
LW	S5,	0x0004	(V0)	;Data extracted
LW	S6,	0x0000	(V1)	;Data at address extracted
ADDIU	V0,	V0,	0x0008	;Interpreter pointer on next code line
;-------------------------------;
ORI	A1,	R0,	0x0080	;Code 80?
BEQL	A0,	A1,		Interpret_Loop
SB	S5,	0x0000	(V1)	;Code 80 done
;-------------------------------;
ORI	A1,	R0,	0x0081	;Code 81?
BEQL	A0,	A1,		Interpret_Loop
SH	S5,	0x0000	(V1)	;Code 81 done
;-------------------------------;
ORI	A1,	R0,	0x0082	;Code 82?
BEQL	A0,	A1,		Interpret_Loop
SW	S5,	0x0000	(V1)	;Code 82 done
;-------------------------------;
OR	T8,	S5,	S6	;T8 = Data at address OR data
AND	T9,	S5,	S6	;T9 = Data at address AND data
;-------------------------------;
ORI	A1,	R0,	0x0083	;Code 83?
BEQL	A0,	A1,		Interpret_Loop
SW	T8,	0x0000	(V1)	;Code 83 done
;-------------------------------;
ORI	A1,	R0,	0x0084	;Code 84?
BEQL	A0,	A1,		Interpret_Loop
SW	T9,	0x0000	(V1)	;Code 84 done
;-------------------------------;
ORI	A1,	R0,	0x0085	;Code 85?
BNE	A0,	A1,		Code_86
OR	S2,	S5,	R0	;Count extracted
				Type_85_Loop:
BEQZ	S2,			Interpret_Loop
LD	S5,	0x0000	(V0)	;
SD	S5,	0x0000	(V1)	;
ADDIU	V0,	V0,	0x0008	;
ADDIU	V1,	V1,	0x0008	;
J				TRAINER_OFFSET + Type_85_Loop
ADDIU	S2,	S2,	-0x0001	;Code 85 done
;-------------------------------;
				Code_86:
ORI	A1,	R0,	0x0086	;Code 86?
BNE	A0,	A1,		Code_87
;-------------------------------;
LB	A1,	0x0000	(V0)	;Check code type of next line
ANDI	A1,	A1,	0x00F0	;
ORI	A2,	R0,	0x0090	;Compare to code type 9
BNE	A1,	A2,		Interpret_Loop
;-------------------------------;
LW	S7,	0x0004	(V0)	;Latter half of next code line loaded
ANDI	S0,	S7,	0xFFFF	;Increment extracted
SLL	S0,	S0,	0x10	;
SRA	S0,	S0,	0x10	;Increment is now signed halfword
SRA	S3,	S7,	0x10	;Offset extracted as signed halfword
LW	S7,	0x0000	(V0)	;First half of next code line loaded
ANDI	S2,	S7,	0xFFFF	;Count extracted
SRL	S7,	S7,	0x10	;
ANDI	S1,	S7,	0x0003	;Mask extracted
ORI	T8,	R0,	0x0003	;Check if mask is invalid
BEQL	S1,	T8,		Code_86_Invalid_Mask
ORI	S1,	R0,	0x0000	;Mask fixed
				Code_86_Invalid_Mask:
;-------------------------------;
				Type_86_Loop:
BEQZL	S2,			Interpret_Loop
ADDIU	V0,	V0,	0x0008	;Interpreter pointer updated
;-------------------------------;
ORI	T8,	R0,	0x0000	;Check for byte write
BEQL	S1,	T8,		Code_86_Write_Byte
SB	S5,	0x0000	(V1)	;Handle byte repeats
				Code_86_Write_Byte:
;-------------------------------;
ORI	T8,	R0,	0x0001	;Check for halfword write
BEQL	S1,	T8,		Code_86_Write_Halfword
SH	S5,	0x0000	(V1)	;Handle halfword repeats
				Code_86_Write_Halfword:
;-------------------------------;
ORI	T8,	R0,	0x0002	;Check for word write
BEQL	S1,	T8,		Code_86_Write_Word
SW	S5,	0x0000	(V1)	;Handle word repeats
				Code_86_Write_Word:
;-------------------------------;
ADDU	V1,	V1,	S3	;Increment address by offset
ADDU	S5,	S5,	S0	;Increment data by increment
J				TRAINER_OFFSET + Type_86_Loop
ADDIU	S2,	S2,	-0x0001	;Code 86 done
				Code_87:
ORI	A1,	R0,	0x0087	;
BNE	A0,	A1,		Interpret_Loop
SRL	S5,	V0,	0x02	;
LUI	S7,		0x2800	;
XOR	S5,	S5,	S7	;S5 = J instruction to next code line
J				TRAINER_OFFSET + Skip_Until_Terminator
SW	S5,	0x0000	(V1)	;J instruction written
;-------------------------------;Next instruction is safe to execute in delay slot
;-------------------------------;
;	CONDITION CODES		;
;-------------------------------;
				Type_DX:
LW	S5,	0x0004	(V0)	;Data extracted
ANDI	A1,	A0,	0x0003	;Extract width bits from code type
ORI	A2,	R0,	0x0003	;Check for invalid width
BEQ	A1,	A2,		Type_DX_Invalid_Width
ORI	S4,	R0,	0x0001	;Execution state set to true
SLL	T8,	S5,	0x10	;
SRA	T8,	T8,	0x10	;T8 is signed halfword data
SLL	T9,	S5,	0x18	;
SRA	T9,	T9,	0x18	;T9 is signed byte data
SLL	S0,	S6,	0x10	;
SRA	S0,	S0,	0x10	;S0 is signed halfword data at address
SLL	S1,	S6,	0x18	;
SRA	S1,	S1,	0x18	;S1 is signed byte data at address
ORI	A2,	R0,	0x0002	;
BEQ	A1,	A2,		Type_DX_Word
LW	S6,	0x0000	(V1)	;
ORI	A2,	R0,	0x0001	;
BNE	A1,	A2,		Type_DX_Byte
NOP
;-------------------------------;Halfword:
OR	S5,	T8,	R0	;Data = halfword:data
J				TRAINER_OFFSET + Type_DX_Halfword
LH	S6,	0x0000	(V1)	;Data at address = halfword:data at address
				Type_DX_Byte:
OR	S5,	T9,	R0	;Data = byte:data
LB	S6,	0x0000	(V1)	;Data at address = byte:data at address
				Type_DX_Halfword:
				Type_DX_Word:
;-------------------------------;Data and data at address are appropriately formatted
ANDI	A1,	A0,	0x000C	;Extract compare type bits from code type
BEQZ	A1,			Must_Be_Equal
ORI	A2,	R0,	0x0004	;
BEQ	A1,	A2,		Must_Be_Different
ORI	A2,	R0,	0x0008	;
BEQ	A1,	A2,		Must_Be_Less
NOP
J				TRAINER_OFFSET + Must_Be_More
NOP
				Must_Be_Equal:
BNEL	S5,	S6,		Execution_Status_Analysis
OR	S4,	R0,	R0	;
J				TRAINER_OFFSET + Execution_Status_Analysis
NOP
				Must_Be_Different:
BEQL	S5,	S6,		Execution_Status_Analysis
OR	S4,	R0,	R0	;
J				TRAINER_OFFSET + Execution_Status_Analysis
NOP
				Must_Be_Less:
SUB	S5,	S5,	S6	;
BGEZL	S5,			Execution_Status_Analysis
OR	S4,	R0,	R0	;
J				TRAINER_OFFSET + Execution_Status_Analysis
NOP
				Must_Be_More:
SUB	S5,	S5,	S6	;
BLEZL	S5,			Execution_Status_Analysis
OR	S4,	R0,	R0	;
J				TRAINER_OFFSET + Execution_Status_Analysis
;-------------------------------;
				Type_DX_Invalid_Width:
				Mask_Compare_Codes:
SRL	S1,	S5,	0x10	;Mask extracted
AND	S5,	S5,	S1	;Data masked
LH	S6,	0x0000	(V1)	;Data at address loaded as halfword
AND	S6,	S6,	S1	;Data at address masked
ANDI	A1,	A0,	0x000C	;Extract mask compare execution requirement type bits from code type
BEQZ	A1,			Must_Be_Equal
ORI	A2,	R0,	0x0004	;
BEQ	A1,	A2,		Must_Be_Different
NOP
J				TRAINER_OFFSET + Interpret_Loop
ADDIU	V0,	V0,	0x0008	;Invalid code type
;-------------------------------;
				Execution_Status_Analysis:
BNEZ	S4,			Interpret_Loop
ADDIU	V0,	V0,	0x0008	;Interpreter pointer updated
J				TRAINER_OFFSET + Code_Skip_Init
ORI	S7,	R0,	0x0001	;Set "can increment terminator count" to true
;-------------------------------;These next few lines are for special codes which use terminators
;-------------------------------;but ignore potential conditional codes in between
				Skip_Until_Terminator:
ORI	S4,	R0,	0x0000	;Set execution status to false
J				TRAINER_OFFSET + Code_Skip_Init
ORI	S7,	R0,	0x0000	;Set "can increment terminator count" to false
;-------------------------------;
				Code_Skip_Init:
ADDIU	A3,	R0,	-0x0001	;Code list terminator
ORI	S0,	R0,	0x000D	;Conditional type
LUI	S3,		0xE000	;Terminator code value
ORI	S4,	R0,	0x0001	;Number of terminators to reach
ORI	T8,	R0,	0x00DB	;First invalid conditional type
ORI	T9,	R0,	0x00DF	;Second invalid conditional type
;-------------------------------;V0 points to next code
				False_Exec_Next_Code_Loop:
LW	S6,	0x0000	(V0)	;
BEQ	S6,	A3,		Exit
NOP
BEQZ	S7,			Only_Check_For_Terminator
SRL	S1,	S6,	0x1C	;
BNE	S0,	S1,		Not_Conditional
SRL	S1,	S6,	0x18	;
BEQ	S1,	T8,		Not_Conditional
NOP
BEQ	S1,	T9,		Not_Conditional
NOP
;-------------------------------;Is conditional; increment S4
ADDIU	S4,	S4,	0x0001	;
				Only_Check_For_Terminator:
				Not_Conditional:
BEQL	S3,	S6,		Terminator_Encountered
ADDIU	S4,	S4,	-0x0001	;Decrement number of terminators that must be encountered
				Terminator_Encountered:
BEQZL	S4,			Interpret_Loop
ADDIU	V0,	V0,	0x0008	;
J				TRAINER_OFFSET + False_Exec_Next_Code_Loop
ADDIU	V0,	V0,	0x0008	;
;-------------------------------;
;	SPECIAL CODES		;
;-------------------------------;
				Type_CX:
ADDIU	V0,	V0,	0x0008	;Interpreter pointer updated
LA	S5,			0x80000000 + TRAINER_OFFSET + Data
SD	V0,	0x0000	(S5)	;Back up interpreter pointer
SD	RA,	0x0008	(S5)	;Back up return address
LUI	RA,			%hi(0x80000000 + TRAINER_OFFSET + Type_CX_Return)
JR	V0			;Jump to user's code
ADDIU	RA,	RA,		%lo(0x80000000 + TRAINER_OFFSET + Type_CX_Return)
				Type_CX_Return:
LA	S5,			0x80000000 + TRAINER_OFFSET + Data
LD	V0,	0x0000	(S5)	;Interpreter pointer restored
J				TRAINER_OFFSET + Skip_Until_Terminator
LD	RA,	0x0008	(S5)	;Return address restored

;;;	End trainer		;;;

				Exit:
LD	V0,	0x0028	(K0)	;
LD	V1,	0x0030	(K0)	;
LD	A0,	0x0038	(K0)	;
LD	A1,	0x0040	(K0)	;
LD	A2,	0x0048	(K0)	;
LD	A3,	0x0050	(K0)	;
LD	T3,	0x0070	(K0)	;
LD	T4,	0x0078	(K0)	;
LD	T5,	0x0080	(K0)	;
LD	T6,	0x0088	(K0)	;
LD	T7,	0x0090	(K0)	;
LD	S0,	0x0098	(K0)	;
LD	S1,	0x00A0	(K0)	;
LD	S2,	0x00A8	(K0)	;
LD	S3,	0x00B0	(K0)	;
LD	S4,	0x00B8	(K0)	;
LD	S5,	0x00C0	(K0)	;
LD	S6,	0x00C8	(K0)	;
LD	S7,	0x00D0	(K0)	;
LD	T8,	0x00D8	(K0)	;
J				0x00075100
LD	T9,	0x00E0	(K0)	;
				Data:
